﻿using System.Linq;
using MvcDesign.Core.CoreDataModel;
using MvcDesign.Core.DataAccess;

namespace MvcDesign.Core.CoreServices.Security
{
    public class CustomRoleProvider : System.Web.Security.RoleProvider
    {
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

            ApplicationName = config["applicationName"];
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            using (var domainModel = new DataContext())
            {
                var user = domainModel.GetCollection<User>().SingleOrDefault(m => m.Name == username);
                var role = domainModel.GetCollection<UserRole>().SingleOrDefault(m => m.Name == roleName);
                if (user == null || role == null)
                {
                    return false;
                }
                return domainModel.GetCollection<UserRoleMapping>().Any(m => m.RoleId == role.Id && m.UserId == user.Id);
            }
        }

        public override string[] GetRolesForUser(string username)
        {
            using (var domainModel = new DataContext())
            {
                var userId = domainModel.GetCollection<User>().Where(u => u.Name == username).Select(u => u.Id).SingleOrDefault();
                if (!userId.HasValue)
                {
                    return new string[0];
                }
                var mappings = domainModel.GetCollection<UserRoleMapping>().Where(m => m.UserId == userId.Value).Select(m => m.RoleId).ToList();
                return domainModel.GetCollection<UserRole>().Where(r => mappings.Contains(r.Id.Value)).Select(r => r.Name).ToArray();
            }
        }

        public override void CreateRole(string roleName)
        {
            using (var domainModel = new DataContext())
            {
                if (domainModel.GetCollection<UserRole>().Any(r => r.Name == roleName))
                {
                    return;
                }

                var role = domainModel.GetCollection<UserRole>().Create();
                role.Name = roleName;
                domainModel.SaveChanges();
            }
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            using (var domainModel = new DataContext())
            {
                var role = domainModel.GetCollection<UserRole>().FirstOrDefault(r => r.Name == roleName);

                if (role == null)
                {
                    return false;
                }

                foreach (var mapping in domainModel.GetCollection<UserRoleMapping>().Where(m => m.RoleId == role.Id))
                {
                    domainModel.GetCollection<UserRoleMapping>().Remove(mapping);
                }

                domainModel.GetCollection<UserRole>().Remove(role);
                domainModel.SaveChanges();

                return true;
            }
        }

        public override bool RoleExists(string roleName)
        {
            using (var domainModel = new DataContext())
            {
                return domainModel.GetCollection<UserRole>().Any(r => r.Name == roleName);
            }
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            using (var domainModel = new DataContext())
            {
                var roles = domainModel.GetCollection<UserRole>().Where(r => roleNames.Any(rn => rn == r.Name)).ToList();
                foreach (var user in domainModel.GetCollection<User>().Where(u => usernames.Any(un => un == u.Name)))
                {
                    if (!user.Id.HasValue)
                    {
                        continue;
                    }

                    foreach (var role in roles)
                    {
                        if (!role.Id.HasValue)
                        {
                            continue;
                        }

                        if (!domainModel.GetCollection<UserRoleMapping>().Any(m => m.UserId == user.Id && m.RoleId == role.Id))
                        {
                            domainModel.GetCollection<UserRoleMapping>().Add(new UserRoleMapping {RoleId = role.Id.Value, UserId = user.Id.Value});
                        }
                    }
                }
                domainModel.SaveChanges();
            }
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            using (var domainModel = new DataContext())
            {
                var roles = domainModel.GetCollection<UserRole>().Where(r => roleNames.Any(rn => rn == r.Name)).ToList();
                foreach (var user in domainModel.GetCollection<User>().Where(u => usernames.Any(un => un == u.Name)))
                {
                    if (!user.Id.HasValue)
                    {
                        continue;
                    }

                    foreach (var role in roles)
                    {
                        if (!role.Id.HasValue)
                        {
                            continue;
                        }

                        if (!domainModel.GetCollection<UserRoleMapping>().Any(m => m.UserId == user.Id && m.RoleId == role.Id))
                        {
                            domainModel.GetCollection<UserRoleMapping>().Add(new UserRoleMapping { RoleId = role.Id.Value, UserId = user.Id.Value });
                        }
                    }
                }
                domainModel.SaveChanges();
            }
        }

        public override string[] GetUsersInRole(string roleName)
        {
            using (var domainModel = new DataContext())
            {
                var role = domainModel.GetCollection<UserRole>().Where(r => r.Name == roleName).Select(r => r.Id).SingleOrDefault();
                if (role == null)
                {
                    return new string[0];
                }

                var mappings = domainModel.GetCollection<UserRoleMapping>().Where(m => m.RoleId == role).Select(m => m.UserId);

                return domainModel.GetCollection<User>().Where(u => !mappings.Contains(u.Id.Value)).Select(u => u.Name).ToArray();
            }
        }

        public override string[] GetAllRoles()
        {
            using (var domainModel = new DataContext())
            {
                return domainModel.GetCollection<UserRole>().Select(r => r.Name).ToArray();
            }
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            using (var domainModel = new DataContext())
            {
                var roleId = domainModel.GetCollection<UserRole>().Where(r => r.Name == roleName).Select(r => r.Id.Value).Single();
                
                var userIds = domainModel.GetCollection<UserRoleMapping>().Where(m => m.RoleId == roleId).Select(m => m.UserId).ToArray();

                return domainModel.GetCollection<User>().Where(u => userIds.Contains(u.Id.Value) && u.Name.Contains(usernameToMatch)).Select(u => u.Name).ToArray();
            }
        }

        public override string ApplicationName { get; set; }
    }
}